// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: leon

#include <unordered_map>
#include <cmath>
#include <cxxopts.hpp>

#include "coin/proto/coin_order_gateway.pb.h"
#include "coin2/app/driver.h"
#include "coin2/base/config/app_config.h"
#include "coin2/base/config/args.h"
#include "coin2/base/log.h"
#include "coin2/base/time.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/strategy/strategy.h"

using namespace coin2::exchange::base::symbology;
using namespace coin2::base::config;
using namespace coin2::exchange::feed;
using namespace coin2::app;
using coin2::exchange::base::feed::FeedUpdate;
using coin2::exchange::base::order::OrderUpdate;

class PriceCheckerStrategy : public ::coin2::strategy::IStrategy {
  void InjectFeed(const coin2::feed::mgr::impl::FeedSubsystemManager*) override {
    LOG(INFO) << "Printer::InjectFeed() is called";
  }
  void onIndexFeed(const FeedUpdate& upd) override {
    auto& index = upd.index();
    const std::string& norm = upd.product().relative_norm();
    if (norm_to_book_price_.find(norm) != std::end(norm_to_book_price_) &&
        norm_to_index_price_.find(norm) == std::end(norm_to_index_price_)) {
      if (std::abs(norm_to_book_price_[norm] - index.price) / norm_to_book_price_[norm] > 0.05) {
        LOG(ERROR) << "Price abnormal [" << norm << "]: book "
                   << norm_to_book_price_[norm] << " vs index "
                   << index.price;
      }
      norm_to_index_price_[norm] = index.price;
    }
  }
  void onBookFeed(const FeedUpdate& upd) override {
    auto& book = upd.book();
    norm_to_book_price_[upd.product().relative_norm()] = (book.Bid0()->price + book.Ask0()->price) / 2;
  }

 private:
  std::unordered_map<std::string, double> norm_to_book_price_;
  std::unordered_map<std::string, double> norm_to_index_price_;
};

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);

  DriverConfig app;

  cxxopts::Options opt("Price checker", "Check price is reasonable.");
  AddDriverOptions(&opt);
  AddIntervalOptions(&opt);
  AddProductOptions(&opt);

  auto res = opt.parse(argc, argv);
  ParseDriverOptions(res, &app);
  ParseIntervalOptions(
      res,
      app.mutable_feed()->mutable_common()->mutable_archive()->mutable_interval());
  ParseProductOptions(res, app.mutable_feed());

  LOG(INFO) << app.DebugString();

  PriceCheckerStrategy checker;
  Driver driver(app, &checker);
  driver.Run();

  return 0;
}
